home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Mac Power 1997 December
/
MACPOWER-1997-12.ISO.7z
/
MACPOWER-1997-12.ISO
/
AMUG
/
PROGRAMMING
/
Raven 1.2.sit
/
Raven 1.2
/
Source
/
Foundation
/
Common
/
ZNumbers.cpp
< prev
next >
Wrap
Text File
|
1997-06-21
|
13KB
|
511 lines
/*
* File: ZNumbers.cpp
* Summary: Number related functions.
* Written by: Jesse Jones
*
* Copyright ゥ 1996-1997 Jesse Jones.
* For conditions of distribution and use, see copyright notice in ZTypes.h
*
* Change History (most recent first):
*
* <4> 4/27/97 JDJ Added int and double versions of Random.
* <3> 4/15/97 JDJ Renamed EqualReal Equal.
* <2> 2/23/97 JDJ EqualReal ASSERTs that tolerance and typical are valid.
* <1> 1/13/96 JDJ Created
*/
#include <ZNumbers.h>
#include <Events.h>
#include <Fp.h>
#include <Limits.h>
#include <ZDebug.h>
#include <ZTypes.h>
#include <ZUnitTest.h>
// ===================================================================================
// Internal Functions
// The random number algorithm is from D. E. Knuth, _The Stanford GraphBase_,
// Addison-Wesley, ISBN 0-201-54275-7. Note that the generator is much better
// than a simple linear congruential algorithm, but about 13% slower.
// ===================================================================================
long gb_flip_cycle(void);
long gb_init_rand(long seed);
long gb_unif_rand(long m);
int gb_flip(void);
const unsigned long kTwo_to_the_31 = 0x80000000UL;
//---------------------------------------------------------------
//
// Private Declarations
//
//---------------------------------------------------------------
static long A[56] = {-1};
long sFlipSeed = gb_init_rand((long) TickCount());
//---------------------------------------------------------------
//
// External Declarations
//
//---------------------------------------------------------------
long *gb_fptr = A;
#define gb_next_rand() (*gb_fptr >= 0 ? *gb_fptr-- : gb_flip_cycle())
#define mod_diff(x,y) (((x) - (y)) & 0x7FFFFFFF)
//---------------------------------------------------------------
//
// gb_flip_cycle
//
//---------------------------------------------------------------
long gb_flip_cycle(void)
{
register long *ii, *jj;
for (ii = &A[1], jj = &A[32]; jj <= &A[55]; ii++, jj++)
*ii = mod_diff(*ii, *jj);
for (jj = &A[1]; ii<= &A[55]; ii++, jj++)
*ii = mod_diff(*ii, *jj);
gb_fptr = &A[54];
return A[55];
}
//---------------------------------------------------------------
//
// gb_init_rand
//
//---------------------------------------------------------------
long gb_init_rand(long seed)
{
register long i;
register long prev = seed, next = 1;
seed = prev = mod_diff(prev, 0);
A[55] = prev;
for (i = 21; i; i = (i + 21) % 55) {
A[i] = next;
/* Compute a new next value. */
next = mod_diff(prev, next);
if (seed & 1)
seed = 0x40000000 + (seed >> 1);
else
seed >>= 1;
next = mod_diff(next, seed);
prev = A[i];
}
/* Get the array values ``warmed up''. */
(void) gb_flip_cycle();
(void) gb_flip_cycle();
(void) gb_flip_cycle();
(void) gb_flip_cycle();
(void) gb_flip_cycle();
if (seed == 0)
seed = 1;
return seed; // Hack to enable gb_init_rand to be called before main.
}
//---------------------------------------------------------------
//
// gb_unif_rand
//
//---------------------------------------------------------------
long gb_unif_rand(long m)
{
ASSERT(m > 0);
register long r;
register unsigned long t = kTwo_to_the_31 - (kTwo_to_the_31 % m);
if (m > 1) {
do {
r = gb_next_rand();
} while (t <= (unsigned long) r);
r = r % m;
} else
r = 0;
return r;
}
//---------------------------------------------------------------
//
// gb_flip
//
//---------------------------------------------------------------
int gb_flip(void)
{
int j;
gb_init_rand(-314159L);
if (gb_next_rand() != 119318998) {
DEBUGSTR("Failure on the first try.¥n");
return -1;
}
for (j = 1; j <= 133; j++)
gb_next_rand();
if (gb_unif_rand(0x55555555L) != 748103812) {
DEBUGSTR("Failure on the second try.¥n");
return -2;
}
/* DEBUGSTR("OK, the gb_flip_routines seem to work.¥n"); */
return 0;
}
#pragma mark -
// ===================================================================================
// Global Functions
// ===================================================================================
//---------------------------------------------------------------
//
// FlipCoin
//
// The algorithm is from the second edition of _Numerical Recipies
// in C_ by Press, Teukolsky, Vetterling, and Flannery and is based
// on "primitive polynomials modulo 2". The polynomial used here
// is x**18 + x**5 + x**2 + x + 1. This is guaranteed to produce
// all possible sequences of 18 bits before repeating.
//
// Numerical Recipies has a table with larger polynomials that
// will yield a larger period but they'll also yield (occasionally)
// longer runs returning the same result.
//
//---------------------------------------------------------------
bool FlipCoin()
{
const ulong kBit1 = 1;
const ulong kBit2 = 2;
const ulong kBit5 = 16;
const ulong kBit18 = 131072;
const ulong kMask = kBit1 + kBit2 + kBit5;
ASSERT(sFlipSeed != 0);
if (sFlipSeed & kBit18) {
sFlipSeed = (long) (((sFlipSeed ^ kMask) << 1) | kBit1);
return true;
} else {
sFlipSeed <<= 1;
if (sFlipSeed == 0)
sFlipSeed = 1;
return false;
}
}
//---------------------------------------------------------------
//
// Equal
//
//---------------------------------------------------------------
bool Equal(double x, double y, double tolerance, double typical)
{
ASSERT(tolerance >= 0.0);
ASSERT(typical > 0.0);
bool equal = false;
double z = Max(Abs(x), Abs(y));
if (z == __inf())
equal = x == y;
if (!equal) {
if (Abs(x - y)/z <= tolerance)
equal = true;
else if (z/typical <= tolerance)
equal = true;
}
return equal;
}
//---------------------------------------------------------------
//
// SetRandomSeed
//
//---------------------------------------------------------------
void SetRandomSeed(long seed)
{
sFlipSeed = gb_init_rand(seed);
}
//---------------------------------------------------------------
//
// Random (long)
//
//---------------------------------------------------------------
long Random(long max)
{
return (long) gb_unif_rand(max);
}
//---------------------------------------------------------------
//
// Random (double)
//
//---------------------------------------------------------------
double Random(double max)
{
long n = gb_unif_rand(LONG_MAX);
double x = max*scalb(n, -31);
return x;
}
//---------------------------------------------------------------
//
// Random (long, long)
//
//---------------------------------------------------------------
long Random(long min, long max)
{
long range = max - min;
return min + gb_unif_rand(range);
}
//---------------------------------------------------------------
//
// Random (double, double)
//
//---------------------------------------------------------------
double Random(double min, double max)
{
double range = max - min;
return min + Random(range);
}
#pragma mark -
// ===================================================================================
// Unit Test
// ===================================================================================
//---------------------------------------------------------------
//
// GetRandom
//
// A function to help test the random number testers. It does this
// by providing some low quality random number generators that will
// hopefully do poorly on our tests.
//
//---------------------------------------------------------------
#if DEBUG
static short GetRandom(short max)
{
#if 1
// Use the real random number generator.
short x = Random(max);
#elif 0
// Use a real lousy random number generator.
gRandomSeed = Abs(7*gRandomSeed) % 211;
long y = Abs(gRandomSeed) % 100; // y is in [0, 99]
short x = (short) (max*y/100);
#elif 0
// Return the next integer in the range.
short x = (short) (Abs(gRandomSeed++) % max);
#endif
return x;
}
#endif // DEBUG
//---------------------------------------------------------------
//
// PassesChiSqr
//
// See Algorithms by Sedgewick.
//
//---------------------------------------------------------------
#if DEBUG
static bool PassesChiSqr(short max)
{
long i;
const long kNoIterations = 10L*max;
long* count = new long[max];
for (i = 0; i < max; i++)
count[i] = 0;
for (i = 0; i < kNoIterations; i++) {
short x = GetRandom(max);
if (x >= 0 && x < max)
count[x] += 1;
else
TRACE("Random(%d) returned %d.¥n", max, x);
}
long t = 0;
for (i = 0; i < max; i++)
t += count[i]*count[i];
long chiSqr = (max*t/kNoIterations) - kNoIterations;
long toler = (long) (2*sqrt((float) max));
#if 0
TRACE("Chi-square for Random(%d) = %d¥n", max, chiSqr);
TRACE("It should be within %d of %d¥n", toler, max);
#endif
delete [] count;
return Abs(chiSqr - max) <= toler;
}
#endif // DEBUG
//---------------------------------------------------------------
//
// TestSpread
//
// Here we check to see if the values returned by the random
// number generator are reasonably spread out. We do this by
// using the chi-squared test. This can fail about once in ten
// times, so we'll write an error message if it fails more than
// twice.
//
//---------------------------------------------------------------
#if DEBUG
static void TestSpread(short max)
{
short failCount = 0;
for (short i = 0; i < 10; i++)
if (!PassesChiSqr(max))
failCount++;
if (failCount > 2) {
TRACE("Random(%d) failed the chi-squared test %d times.¥n", max, failCount);
TRACE("Note that failing the test once is perfectly normal.¥n¥n");
}
}
#endif // DEBUG
//---------------------------------------------------------------
//
// HasACycle
//
// Writes a message if cycleLen values starting at startIndex
// form a cycle (ie the next set of cycleLen values is the same
// as the first).
//
//---------------------------------------------------------------
#if DEBUG
static bool HasACycle(short values[], short noValues, short cycleLen, short startIndex, short max)
{
short entry = 0; // index into cycle
short repeatCount = 0; // number of times cycle has been repeated
short index = (short) (startIndex + cycleLen);// index into next value to be checked
bool cycling = true; // turns false when cycle is broken
while (index < noValues && cycling) {
if (values[startIndex + entry++] != values[index++])
cycling = false;
else if (entry == cycleLen) {
entry = 0;
repeatCount++;
}
}
if (repeatCount > 0) {
TRACE("A cycle of length %d was repeated %d", cycleLen, repeatCount);
TRACE(" times with Random(%d).¥n", max);
}
return repeatCount > 0;
}
#endif // DEBUG
//---------------------------------------------------------------
//
// TestForCycles
//
// A good random number generator should generate numbers that
// are independant of the numbers it cranked out before. In this
// test we check to see if the random number generator degenerates
// into cyclic behavior. For example, a bad random number generator
// might fall into a cycle of length 4 for Random(3): 011201120112.
//
// The code below checks for cycles of length max and up. (If the
// random number generator falls into a cycle with length smaller
// than max it will fail the frequency test above).
//
//---------------------------------------------------------------
#if DEBUG
static void TestForCycles(short max)
{
const short kNoIterations = 1000;
short values[kNoIterations];
for (short i = 0; i < kNoIterations; i++)
values[i] = GetRandom(max);
const short kMaxCycle = kNoIterations/3;
for (short cycleLen = max; cycleLen < kMaxCycle; cycleLen++)
for (short startIndex = 0; startIndex < kNoIterations - cycleLen; startIndex++)
if (HasACycle(values, kNoIterations, cycleLen, startIndex, max))
break;
}
#endif // DEBUG
//---------------------------------------------------------------
//
// TestRandom
//
//---------------------------------------------------------------
#if DEBUG
static void TestRandom()
{
while (gb_flip() < 0) // Random
;
TestSpread(2);
TestSpread(10);
TestSpread(105);
TestForCycles(10);
TestForCycles(23);
TestForCycles(100);
// ・・・ハneed to test FlipCoin as well
TRACE("Completed random test.¥n¥n");
}
static TUnitTestRegistrar sRandReg("Random", TestRandom);
#endif // DEBUG